﻿using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using StyleMVVM.BootstrapperHelpers;
using StyleMVVM.Configuration;
using StyleMVVM.DependencyInjection;
using StyleMVVM.DependencyInjection.Impl;
using StyleMVVM.Logging;
#if NETFX_CORE
using Windows.UI.Xaml;

#else
#endif
using StyleMVVM.ViewModel.Impl;

namespace StyleMVVM
{
	/// <summary>
	/// This is the base implementation for IBootstrapper. 
	/// It is also where the global instance of the IBootstrapper lives.
	/// </summary>
	public sealed class Bootstrapper : IBootstrapper
	{
		private bool isReady;
		private static volatile IBootstrapper instance;
		private static readonly object lockObject = new object();

		/// <summary>
		/// Default constructor registers this bootstraper as the default Instance
		/// and then create the container and register the default factories
		/// </summary>
		public Bootstrapper()
		{
			Instance = this;

			Container = new DependencyInjectionContainer();

			Container.RegisterFactory(new PageActivationStrategyFactor());
			Container.RegisterFactory(new ClientBaseActivationStrategyFactory());

			RegisterStyleAssembly = true;
		}

		/// <summary>
		/// Constructor where you provide the Container instead of allowing the bootstrapper to create one
		/// </summary>
		/// <param name="container"></param>
		public Bootstrapper(IDependencyInjectionContainer container)
		{
			Instance = this;

			Container = container;

			if (Container == null)
			{
				Container = new DependencyInjectionContainer();
			}

			Container.RegisterFactory(new PageActivationStrategyFactor());
			Container.RegisterFactory(new ClientBaseActivationStrategyFactory());

			RegisterStyleAssembly = true;
		}

		/// <summary>
		/// Is there an Instance created
		/// </summary>
		public static bool HasInstance
		{
			get { return instance != null; }
		}

		/// <summary>
		/// Returns true if there is a bootstrapper and it is ready
		/// </summary>
		public static bool InstanceIsReady
		{
			get
			{
				if (instance != null && instance.IsReady)
				{
					return true;
				}

				return false;
			}
		}

		/// <summary>
		/// Default instance of the boostrapper.
		/// This Property will create on if one does not exist and register the main apps exports
		/// This is not the recommend way of doing it.
		/// </summary>
		public static IBootstrapper Instance
		{
			get
			{
				if (instance == null)
				{
					lock (lockObject)
					{
						if (instance == null)
						{
							var tempInstance = new Bootstrapper();

							try
							{
#if NETFX_CORE
								if (Window.Current != null && Window.Current.Content != null)
								{
									tempInstance.Container.RegisterAssembly(
										Window.Current.Content.GetType().GetTypeInfo().Assembly);
								}
#elif WINDOWS_PHONE
								if (Application.Current.RootVisual != null)
								{
									tempInstance.Container.RegisterAssembly(
										Application.Current.RootVisual.GetType().GetTypeInfo().Assembly);
								}
#elif DOT_NET
								if (Application.Current != null)
								{
									tempInstance.Container.RegisterAssembly(
										Application.Current.GetType().GetTypeInfo().Assembly);
								}
								else
								{
									Assembly loadAssembly = Assembly.GetEntryAssembly();

									if (loadAssembly != null)
									{
										tempInstance.Container.RegisterAssembly(loadAssembly);
									}
								}
#endif
								tempInstance.Start();

								instance = tempInstance;
							}
							catch (Exception exp)
							{
								Logger.Error(" there was an exception thrown while trying to create the bootstrapper",
								             typeof(Bootstrapper).FullName,
								             exp);
							}
						}
					}
				}

				return instance;
			}
			set
			{
				lock (lockObject)
				{
					if (instance == null || value == null)
					{
						instance = value;
					}
				}
			}
		}

		/// <summary>
		/// Should the IDependencyInjectionContainer register the StyleMVVM library for exports
		/// This is by default true and shouldn't be changed unless you are prepared to 
		/// handle all needed export types.
		/// </summary>
		private bool RegisterStyleAssembly { get; set; }

		/// <summary>
		/// The DI container for the bootstrapper
		/// </summary>
		public IDependencyInjectionContainer Container { get; set; }

		public bool IsReady
		{
			get { return isReady; }
			private
				set
			{
				isReady = value;

				Logger.Info("Bootstrapper is Ready");
			}
		}

		public bool JSApplication { get; set; }

		/// <summary>
		/// Starts the bootstrapper
		/// </summary>
		public void Start()
		{
			Start(true);
		}

		/// <summary>
		/// Starts the bootstrapper
		/// </summary>
		public void Start(bool launchUI)
		{
			Logger.Info("Bootstrapper Starting");

			if (JSApplication)
			{
				Container.BlackListExportType(
					typeof(AppSettingConfigurationStorageProvider));
			}

			if (RegisterStyleAssembly)
			{
				Container.RegisterAssembly(GetType().GetTypeInfo().Assembly);
			}

#if WINDOWS_PHONE
            Container.BlackListExportType(typeof(ViewModelNavigationBinder));
#endif

			Container.Start(launchUI);

			IsReady = true;
		}

		public void Launched()
		{
			Container.Launch();
		}

		/// <summary>
		/// Shuts the bootstrapper down
		/// </summary>
		public void Shutdown()
		{
			Logger.Info("Bootstrapper Shutting down");

			Container.Shutdown();

			if (instance == this)
			{
				Instance = null;
			}
		}
	}
}